﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Marmat.Template.WinForm
{
    /// <summary>
    /// 对象转换工厂
    /// </summary>
    public class ObjectConversion
    {
        private readonly Dictionary<Type, string> primaryKeyCollection = new Dictionary<Type, string>();
        private readonly Dictionary<Type, Func<Type, MemberInfo, bool>> memberBuilderCollection = new Dictionary<Type, Func<Type, MemberInfo, bool>>();
        internal readonly Dictionary<Type, ObjectConversionProperties> propertiesCollection = new Dictionary<Type, ObjectConversionProperties>();

        /// <summary>
        /// 注册类型及类型的主键。
        /// </summary>
        /// <typeparam name="Type">需要注册的类型。</typeparam>
        /// <param name="primaryName">主键名称</param>
        public ObjectConversionProperties<Type> Register<Type>(string primaryName) where Type : class
        {
            var properties = new ObjectConversionProperties<Type>();
            properties.Conversion = this;
            properties.PrimaryName = primaryName;
            return Register(primaryName, properties);
        }

        /// <summary>
        /// 注册类型及类型的主键。
        /// </summary>
        /// <typeparam name="Type">需要注册的类型。</typeparam>
        /// <typeparam name="Properties">需要注册的类型。</typeparam>
        /// <param name="primaryName">主键名称</param>
        public ObjectConversionProperties<Type> Register<Type, Properties>(string primaryName) where Type : class where Properties : ObjectConversionProperties<Type>, new()
        {
            var properties = new Properties();
            properties.Conversion = this;
            properties.PrimaryName = primaryName;
            return Register(primaryName, properties);
        }

        /// <summary>
        /// 注册类型及类型的主键。
        /// </summary>
        /// <typeparam name="Type">需要注册的类型。</typeparam>
        /// <param name="expression"></param>
        public ObjectConversionProperties<Type> Register<Type>(Expression<Func<Type, object>> expression) where Type : class
        {
            string primaryName = string.Empty;
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is System.Linq.Expressions.MemberExpression member)
                    primaryName = member.Member.Name;
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression member)
                primaryName = member.Member.Name;
            return this.Register<Type>(primaryName);
        }

        /// <summary>
        /// 注册类型及类型的主键。
        /// </summary>
        /// <typeparam name="Type">需要注册的类型。</typeparam>
        /// <typeparam name="Properties"></typeparam>
        /// <param name="expression"></param>
        public ObjectConversionProperties<Type> Register<Type, Properties>(Expression<Func<Type, object>> expression) where Type : class where Properties : ObjectConversionProperties<Type>, new()
        {
            string primaryName = string.Empty;
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is System.Linq.Expressions.MemberExpression member)
                    primaryName = member.Member.Name;
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression member)
                primaryName = member.Member.Name;

            var properties = new Properties();
            properties.Conversion = this;
            properties.PrimaryName = primaryName;

            return this.Register<Type>(primaryName, properties);
        }

        /// <summary>
        /// 注册类型及类型的主键。
        /// </summary>
        /// <typeparam name="Type">需要注册的类型。</typeparam>
        /// <param name="primaryName">主键名称</param>
        /// <param name="properties">配置类</param>
        public ObjectConversionProperties<Type> Register<Type>(string primaryName, ObjectConversionProperties<Type> properties) where Type : class
        {
            primaryKeyCollection.Add(typeof(Type), primaryName);
            propertiesCollection[typeof(Type)] = properties;
            return properties;
        }


        /// <summary>
        /// 指定序列化字段的规则。
        /// </summary>
        /// <typeparam name="Type"></typeparam>
        /// <param name="expression"></param>
        internal void Serialize<Type>(Expression<Func<Type, object>> expression) where Type : class
        {
            List<string> list = new List<string>();
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is System.Linq.Expressions.MemberExpression member)
                    list.Add(member.Member.Name);
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression member)
                list.Add(member.Member.Name);
            else if (expression.Body is System.Linq.Expressions.NewExpression newExpression)
            {
                foreach (var item in newExpression.Arguments)
                {
                    if (item is System.Linq.Expressions.MemberExpression item_member)
                        list.Add(item_member.Member.Name);
                }
            }
            this.SerializeMember<Type>((type, member) => list.Contains(member.Name));
        }

        /// <summary>
        /// 指定序列化字段的规则。
        /// </summary>
        /// <param name="memberBuilder"></param>
        internal void SerializeMember<Type>(Func<MemberInfo, bool> memberBuilder)
        {
            this.SerializeMember<Type>((type, member) => memberBuilder.Invoke(member));
        }

        /// <summary>
        /// 指定序列化字段的规则。
        /// </summary>
        /// <param name="memberBuilder"></param>
        internal void SerializeMember<Type>(Func<System.Type, MemberInfo, bool> memberBuilder)
        {
            this.memberBuilderCollection[typeof(Type)] = memberBuilder;
        }

        /// <summary>
        /// 注册类型需要忽略的字段。
        /// </summary>
        /// <typeparam name="Type"></typeparam>
        /// <param name="expression"></param>
        internal void Ignore<Type>(Expression<Func<Type, object>> expression) where Type : class
        {
            List<string> list = new List<string>();
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is System.Linq.Expressions.MemberExpression member)
                    list.Add(member.Member.Name);
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression member)
                list.Add(member.Member.Name);
            else if (expression.Body is System.Linq.Expressions.NewExpression newExpression)
            {
                foreach (var item in newExpression.Arguments)
                {
                    if (item is System.Linq.Expressions.MemberExpression item_member)
                        list.Add(item_member.Member.Name);
                }
            }
            this.SerializeMember<Type>(member => !list.Contains(member.Name));
        }

        /// <summary>
        /// 转换
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="source"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        public Dictionary<string, object> Conversion<TEntity>(TEntity source, TEntity current)
        {
            return this.Conversion(typeof(TEntity), source, current);
        }

        /// <summary>
        /// 转换
        /// </summary>
        /// <param name="type"></param>
        /// <param name="source"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        public Dictionary<string, object> Conversion(Type type, object source, object current)
        {
            Dictionary<string, object> dic = null;
            if (source is null && current is not null)
                dic = new Dictionary<string, object>();
            else if (source is not null && current is null)
                dic = new Dictionary<string, object>();

            Func<Type, MemberInfo, bool> expression = null;
            if (this.memberBuilderCollection.ContainsKey(type))
                expression = this.memberBuilderCollection[type];

            // 遍历所有的字段和属性。
            foreach (var member in type.GetMembers().Where(x => x is FieldInfo || x is PropertyInfo))
            {
                // 排除非注册的字段或属性。
                if (expression is null || !expression.Invoke(type, member))
                    continue;

                // 获取源对象的值及当前对象的值
                object sourceValue = null;
                object currentValue = null;
                Type propertyType;
                if (member is FieldInfo field)
                {
                    propertyType = field.FieldType;
                    sourceValue = source is null ? null : field.GetValue(source);
                    currentValue = current is null ? null : field.GetValue(current);
                }
                else if (member is PropertyInfo property)
                {
                    propertyType = property.PropertyType;
                    sourceValue = source is null ? null : property.GetValue(source);
                    currentValue = current is null ? null : property.GetValue(current);
                }
                else continue;

                // 开始比较旧值与新值
                if (propertyType.IsValueType || propertyType == typeof(string)) //如果类型时值对象或字符型的话
                {
                    bool change = this.Compare(member, sourceValue, currentValue);
                    if (change)
                    {
                        if (dic is null) dic = new Dictionary<string, object>();
                        dic[member.Name] = currentValue;
                    }
                }
                else if (this.primaryKeyCollection.ContainsKey(propertyType)) //如果注册的类型中包括该类型，则再次对值进行比较
                {
                    object result = this.Conversion(sourceValue, currentValue);
                    if (result != null)
                    {
                        if (dic is null) dic = new Dictionary<string, object>();
                        dic[member.Name] = result;
                    }
                }
                else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(propertyType))
                {
                    var property_Type = propertyType.GenericTypeArguments[0];
                    ObjectConversionProperties properties = this.propertiesCollection[property_Type];

                    List<object> array = new List<object>();
                    object[] sourceCollection = ((sourceValue ?? Array.Empty<object>()) as System.Collections.IEnumerable).OfType<object>().ToArray();
                    object[] currentCollection = ((currentValue ?? Array.Empty<object>()) as System.Collections.IEnumerable).OfType<object>().ToArray();

                    List<object> addCollection = new List<object>(); //添加的集合
                    Dictionary<object, object> changeCollection = new Dictionary<object, object>();//修改的集合
                    List<object> deleteCollection = new List<object>();//删除的集合

                    #region"遍历源集合，查找修改及删除的对象。"
                    foreach (object sourceItem in sourceCollection)
                    {
                        object primaryValue = this.GetPrimaryValue(sourceItem);
                        object currentItem = currentCollection.FirstOrDefault(x => object.Equals(this.GetPrimaryValue(x), primaryValue));

                        // 如果在当前对象中找到了的话，则获取修改对象，如果没有找到记录，则标记删除
                        if (currentItem != null)
                            changeCollection.Add(sourceItem, currentItem);
                        else
                            deleteCollection.Add(sourceItem);
                    }

                    #endregion

                    #region "遍历当前集合，查找新增的对象。"
                    foreach (var currentItem in currentCollection)
                    {
                        object primaryValue = this.GetPrimaryValue(currentItem);
                        object sourceItem = sourceCollection.FirstOrDefault(x => object.Equals(this.GetPrimaryValue(x), primaryValue));
                        // 如果当前对象在源集合中 没有找到，则代表新增。
                        if (sourceItem is null)
                            addCollection.Add(currentItem);
                    }
                    #endregion

                    if (addCollection.Count > 0)
                        properties.AdditionCollection(property_Type, addCollection, array);
                    if (changeCollection.Count > 0)
                        properties.ChangeCollection(property_Type, changeCollection, array);
                    if (deleteCollection.Count > 0)
                        properties.DeleteCollection(property_Type, deleteCollection, array);

                    if (array.Count > 0)
                    {
                        if (dic is null) dic = new Dictionary<string, object>();
                        dic[member.Name] = array;
                    }
                }

                // 如果有变更，并且已经注册类型的话，则将主键添加到变更记录中。
                if (dic?.Count > 0 && source != null)
                {
                    string primaryName = this.primaryKeyCollection[type];
                    object primaryValue = this.GetPrimaryValue(current);
                    Dictionary<string, object> vals = new Dictionary<string, object>();
                    vals[primaryName] = primaryValue;
                    foreach (var item in dic)
                    {
                        vals[item.Key] = item.Value;
                    }
                    dic = vals;
                }
            }
            return dic;
        }

        /// <summary>
        /// 获取主键值
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public object GetPrimaryValue(object instance)
        {
            Type type = instance.GetType();
            string primaryKeyName = this.primaryKeyCollection[type];
            MemberInfo member = type.GetMember(primaryKeyName).FirstOrDefault();
            if (member is FieldInfo field)
                return field.GetValue(instance);
            else if (member is PropertyInfo property)
                return property.GetValue(instance);
            else
                throw new NullReferenceException($"在获取 {type.Name} 对象主键值时失败");
        }

        /// <summary>
        /// 值对象的比较
        /// </summary>
        /// <param name="member"></param>
        /// <param name="source"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        public virtual bool Compare(MemberInfo member, object source, object current)
        {
            if (object.Equals(source, current))
                return false;
            return true;
        }

        /// <summary>
        /// 比较对象是否变化
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="source"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        public bool HasChanges<TEntity>(TEntity source, TEntity current)
        {
            return this.Conversion(source, current) != null;
        }
    }
}
